import serial
import serial.tools.list_ports
import sys


def list_available_ports():
    """列出所有可用串口"""
    ports = serial.tools.list_ports.comports()
    if not ports:
        print("未找到可用串口设备")
        return []

    print("\n可用串口列表：")
    for idx, port in enumerate(ports, 1):
        print(f"{idx}. {port.device} - {port.description}")
    return [port.device for port in ports]


def open_serial_port(port_name, baudrate=115200):
    """安全打开串口连接"""
    try:
        ser = serial.Serial(
            port=port_name,
            baudrate=baudrate,
            bytesize=serial.EIGHTBITS,
            parity=serial.PARITY_NONE,
            stopbits=serial.STOPBITS_ONE,
            timeout=1
        )
        print(f"\n成功打开串口 {port_name}")
        print("当前配置：")
        print(f"波特率：{ser.baudrate}")
        print(f"数据位：{ser.bytesize}")
        print(f"校验位：{ser.parity}")
        print(f"停止位：{ser.stopbits}")
        return ser
    except serial.SerialException as e:
        print(f"\n错误：无法打开串口 {port_name}")
        print("可能原因：")
        print("1. 串口被其他程序占用")
        print("2. 设备未连接")
        print("3. 驱动程序未安装")
        print(f"详细错误：{str(e)}")
        return None
    except Exception as e:
        print(f"\n未知错误：{str(e)}")
        return None


def send_data(ser, data, is_hex=False):
    """发送数据（支持HEX/ASCII格式）"""
    try:
        if is_hex:
            # 移除空格并验证HEX格式
            data = data.replace(' ', '')
            if not all(c in '0123456789abcdefABCDEF' for c in data):
                print("错误：包含非法HEX字符")
                return False
            if len(data) % 2 != 0:
                print("错误：HEX数据长度必须为偶数")
                return False
            data_bytes = bytes.fromhex(data)
        else:
            data_bytes = data.encode('utf-8')

        bytes_written = ser.write(data_bytes)
        print(f"成功发送 {bytes_written} 字节")
        return True
    except ValueError:
        print("HEX数据格式错误")
    except serial.SerialTimeoutException:
        print("发送超时")
    except Exception as e:
        print(f"发送失败：{str(e)}")
    return False


def receive_data(ser, timeout=5):
    """接收数据并显示"""
    try:
        print(f"\n等待接收数据（超时：{timeout}秒）...")
        start_time = time.time()
        while True:
            if ser.in_waiting > 0:
                data = ser.read(ser.in_waiting)
                print(f"\n接收数据（HEX）：{data.hex(' ')}")
                print(f"接收数据（ASCII）：{data.decode('ascii', errors='replace')}")
                return data

            if time.time() - start_time > timeout:
                print("接收超时")
                return None

            time.sleep(0.1)
    except Exception as e:
        print(f"接收失败：{str(e)}")
        return None


def interactive_menu(ser):
    """交互式操作菜单"""
    while True:
        print("\n请选择操作：")
        print("1. 发送HEX数据")
        print("2. 发送文本数据")
        print("3. 接收数据")
        print("4. 退出程序")

        choice = input("请输入选项数字：").strip()

        if choice == '1':
            hex_data = input("请输入HEX数据（例如A1 B2 C3）：").strip()
            send_data(ser, hex_data, is_hex=True)
        elif choice == '2':
            text_data = input("请输入文本内容：").strip()
            send_data(ser, text_data)
        elif choice == '3':
            receive_data(ser)
        elif choice == '4':
            print("正在关闭串口...")
            ser.close()
            print("程序已退出")
            sys.exit(0)
        else:
            print("无效输入，请重新选择")


if __name__ == "__main__":
    import time

    # 自动检测可用串口
    ports = list_available_ports()
    if not ports:
        sys.exit(1)

    # 用户选择串口
    try:
        port_num = int(input("\n请输入要打开的串口号（数字）：")) - 1
        selected_port = ports[port_num]
    except (ValueError, IndexError):
        print("无效的输入选择")
        sys.exit(1)

    # 打开串口
    ser = open_serial_port(selected_port)
    if not ser:
        sys.exit(1)

    # 进入交互菜单
    try:
        interactive_menu(ser)
    finally:
        if ser.is_open:
            ser.close()
            print("串口连接已关闭")
